Repository: tailcallhq/tailcall (Rust)

Issue:
Validation for missing resolvers at Root level
**Description:**
When a tailcall server is configured without a resolver at the root level, it starts successfully. However, it doesn't respond to any requests, which can be misleading for developers. This behavior should be addressed by enforcing the presence of a resolver at compile time.

**Reproduction Steps:**
1. Configure the tailcall server without a root-level resolver.
2. Start the server.
3. Observe that the server starts successfully without errors.
4. Make a request to the server.
5. Notice the server provides no response.

**Example Configuration with Issue:**
```graphql
type Query {
  user: [User]! # Unresolved root level configuration
}

type User {
  id: ID
  name: String
  posts: [Post] @http(path: "/posts/{{value.id}}") 
}
```

**Proposed Solution:**
If a configuration lacks a resolver at the root level and none can be inferred, a compile-time error should be thrown, clearly indicating the absence of a necessary resolver. This ensures that developers are aware of the missing resolver before runtime.

**Update 1**
This should only be applicable for scenarios where the type of the field is **required**. In a scenario where the field is not required, it's totally fine if there are no resolvers. 


Discussion: /bounty 200$
/attempt #207


<details id="algora-options">
  <summary>Options</summary>
  <ul>
    <li>
      <a href="https://console.algora.io/api/bounties/cln1poutt000imn0f9kfxreqm/cancel-attempt">
        Cancel my attempt
      </a>
    </li>
  </ul>
</details>

/attempt #207

<details id="algora-options">
  <summary>Options</summary>
  <ul>
    <li>
      <a href="https://console.algora.io/api/bounties/cln1poutt000imn0f9kfxreqm/cancel-attempt">
        Cancel my attempt
      </a>
    </li>
  </ul>
</details>

Note: The user @meskill is already attempting to complete issue #207 and claim the bounty. If you attempt to complete the same issue, there is a chance that @meskill will complete the issue first, and be awarded the bounty. We recommend discussing with @meskill and potentially collaborating on the same solution versus creating an alternate solution.
I've just recently noticed that @Nirali-Ahir also attempted to solve the issue and already provided solution.

So I decided to show my current work as a draft pr only that requires some additional work. It handles little more cases but if is not required feel free to close it.



Fix (patch):
diff --git a/src/blueprint/from_config.rs b/src/blueprint/from_config.rs
index af91c16f9e..455bc96bea 100644
--- a/src/blueprint/from_config.rs
+++ b/src/blueprint/from_config.rs
@@ -47,15 +47,17 @@ fn to_directive(const_directive: ConstDirective) -> Valid<Directive> {
   Ok(Directive { name: const_directive.name.node.clone().to_string(), arguments, index: 0 })
 }
 fn to_schema(config: &Config) -> Valid<SchemaDefinition> {
-  let query = config
+  let query_type_name = config
     .graphql
     .schema
     .query
     .as_ref()
-    .validate_some("Query type is not defined".to_string())?;
+    .validate_some("Query root is missing".to_owned())?;
+
+  validate_query(config).validate_or(validate_mutation(config))?;
 
   Ok(SchemaDefinition {
-    query: query.clone(),
+    query: query_type_name.clone(),
     mutation: config.graphql.schema.mutation.clone(),
     directives: vec![to_directive(config.server.to_directive("server".to_string()))?],
   })
@@ -215,6 +217,53 @@ fn to_type(name: &str, list: bool, non_null: bool, list_type_required: bool) ->
   }
 }
 
+fn validate_query(config: &Config) -> Valid<()> {
+  let query_type_name = config
+    .graphql
+    .schema
+    .query
+    .as_ref()
+    .validate_some("Query root is missing".to_owned())?;
+
+  let Some(query) = config.find_type(query_type_name) else {
+    return Valid::fail("Query type is not defined".to_owned()).trace(query_type_name);
+  };
+
+  query
+    .fields
+    .iter()
+    .validate_all(validate_field_has_resolver)
+    .trace(query_type_name)?;
+
+  Ok(())
+}
+
+fn validate_mutation(config: &Config) -> Valid<()> {
+  let mutation_type_name = config.graphql.schema.mutation.as_ref();
+
+  if let Some(mutation_type_name) = mutation_type_name {
+    let Some(mutation) = config.find_type(mutation_type_name) else {
+      return Valid::fail("Mutation type is not defined".to_owned()).trace(mutation_type_name);
+    };
+
+    mutation
+      .fields
+      .iter()
+      .validate_all(validate_field_has_resolver)
+      .trace(mutation_type_name)?;
+  }
+
+  Ok(())
+}
+
+fn validate_field_has_resolver((name, field): (&String, &Field)) -> Valid<()> {
+  if field.has_resolver() {
+    Ok(())
+  } else {
+    Valid::fail("No resolver has been found in the schema".to_owned()).trace(name)
+  }
+}
+
 fn validate_field_type_exist(config: &Config, field: &Field) -> Valid<()> {
   let field_type = &field.type_of;