Skip to content
Advertisement

aws cdk nextjs graphql mutation cannot return null for non-nullable type after adding in new schema field

I have tried to modify the schema to add a ingredients String line. From this code I am pretty much following it to a tee https://github.com/dabit3/next.js-cdk-amplify-workshop you can look in the backend and frontend folders to see all the code

I have built and deployed with cdk

My error is at the bottom

Any help would be great

Here’s the relevant parts of the schema in the backend

# graphql/schema.graphql
type Post @aws_api_key @aws_cognito_user_pools {
  id: ID!
  title: String!
  content: String!
  ingredients: String!
  owner: String!
}

input PostInput {
  id: ID
  title: String!
  ingredients: String!
  content: String!
}

input UpdatePostInput {
  id: ID!
  title: String
  content: String
  ingredients: String
}

Here is my graphql.js in my frontend code

export const getPostById = /* GraphQL */ `
  query getPostById($postId: ID!) {
    getPostById(postId: $postId) {
      id
      title
      content
      ingredients
      owner
    }
  }
`;

export const listPosts = /* GraphQL */ `
  query ListPosts {
    listPosts {
      id
      title
      content
      ingredients
      owner
    }
  }
`;

export const postsByUsername = /* GraphQL */ `
  query PostsByUsername {
    postsByUsername {
      id
      title
      content
      ingredients
      owner
    }
  }
`;

export const createPost = /* GraphQL */ `
  mutation CreatePost($post: PostInput!) {
    createPost(post: $post) {
      id
      title
      content
      ingredients
      owner
    }
  }
`;

Then I have tried the following in the create-post.js in my frontend

// pages/create-post.js
import { withAuthenticator } from "@aws-amplify/ui-react";
import { useState } from "react";
import { API } from "aws-amplify";
import { v4 as uuid } from "uuid";
import { useRouter } from "next/router";
import SimpleMDE from "react-simplemde-editor";
import "easymde/dist/easymde.min.css";
import { createPost } from "../graphql";

const initialState = { title: "", content: "", ingredients: "" };

function CreatePost() {
  const [post, setPost] = useState(initialState);
  const { title, content, ingredients } = post;
  const router = useRouter();
  function onChange(e) {
    setPost(() => ({ ...post, [e.target.name]: e.target.value }));
  }
  async function createNewPost() {
    if (!title || !content || !ingredients) return;
    const id = uuid();
    post.id = id;

    await API.graphql({
      query: createPost,
      variables: { post },
      authMode: "AMAZON_COGNITO_USER_POOLS",
    });
    router.push(`/posts/${id}`);
  }
  return (
    <div style={containerStyle}>
      <h2>Create new Post</h2>
      <input
        onChange={onChange}
        name="title"
        placeholder="Title"
        value={post.title}
        style={inputStyle}
      />
      <input
        onChange={onChange}
        name="ingredients"
        placeholder="Ingredients"
        value={post.ingredients}
        style={inputStyle}
      />
      <SimpleMDE
        value={post.content}
        onChange={(value) => setPost({ ...post, content: value })}
      />
      <button style={buttonStyle} onClick={createNewPost}>
        Create Post
      </button>
    </div>
  );
}

const inputStyle = {
  marginBottom: 10,
  height: 35,
  width: 300,
  padding: 8,
  fontSize: 16,
};
const containerStyle = { padding: "0px 40px" };
const buttonStyle = {
  width: 300,
  backgroundColor: "white",
  border: "1px solid",
  height: 35,
  marginBottom: 20,
  cursor: "pointer",
};
export default withAuthenticator(CreatePost);

I then get the following message when trying to create the post

{
  type: 'Object',
  stack: null,
  data: { listPosts: [ [Object], null, [Object], null ] },
  errors: [
    {
      path: [Array],
      locations: null,
      message: "Cannot return null for non-nullable type: 'String' within parent 'Post' (/listPosts[1]/ingredients)"
    },
    {
      path: [Array],
      locations: null,
      message: "Cannot return null for non-nullable type: 'String' within parent 'Post' (/listPosts[3]/ingredients)"
    }
  ]
}

lambda-fns/listPosts.ts

// lambda-fns/listPosts.ts
const AWS = require('aws-sdk')
const docClient = new AWS.DynamoDB.DocumentClient()

async function listPosts() {
    const params = {
        TableName: process.env.POST_TABLE,
    }
    try {
        const data = await docClient.scan(params).promise()
        return data.Items
    } catch (err) {
        console.log('DynamoDB error: ', err)
        return null
    }
}

export default listPosts

I was missing adding the ingredients in the Post.ts file in my backend

// lambda-fns/Post.ts
type Post = {
    id: string,
    title: string,
    content: string,
    ingredients: string,
    owner: string
}

export default Post

Advertisement

Answer

It seems like there are some debugging details missing from your question. For example, your error message displays the results of a listPosts query, but you don’t show that being called anywhere. Also, that query apparently returns something, so it would be good to see the database entries backing it.

If I had to guess, you have entries in your dynamo table that have missing ingredients fields, which is why you see an error: the query is trying to marshal missing/null fields to a return type of String!, which is by definition non-nullable.

Also, I’m not sure you should be setting id directly on your post state object. Better to copy the object and set the property on the copy, esp since you’re just using it as a parameter.

User contributions licensed under: CC BY-SA
2 People found this is helpful
Advertisement