Introduction to the

This article is part of the Full Stack GraphQL + Flutter Best Practices Article series. GraphQL uses MySQL as its data source to provide an interface for add, delete, change and query operations.

  • Query the post
  • The new post
  • Modify the post
  • Delete the post

Project depend on

  • Initial project

    • If you have completed the “How to Access a Database using TypeORM? You can continue to use the project code
    • If not, click here to download the initial project
  • A connectable database

    • If you have completed the Docker start MySQL container, make sure Docker is running properly
    • If you have other databases available, you will need to replace the connection parameters yourself below

Step 0

Open the file SRC /index.ts and find the main method. Clean up the test code and change it to:

async function main() {

  try {

    const schema = await buildSchema({
      resolvers: [PostResolver],
      dateScalarMode: 'timestamp'
    });

    const connection = await createConnection({
      type: 'mysql',
      host: 'localhost',
      port: 3306,
      username: 'tinylearn',
      password: 'my_password',
      database: 'tinylearn',
      synchronize: true,
      entities: [PostEntity],
    });

    const server = new ApolloServer({
      schema,
      playground: true
    });

    const { url } = await server.listen(4444);

    console.log(`GraphQL Playground available at ${url}`);
    
  } catch (error) {
    console.error(error); }}Copy the code

Run:

$ npm start
Copy the code

On Playground:

Dependency injection

The current posts method looks like this.

@Resolver(Post)
class PostResolver {

  @Query(returns= > [Post])
  async posts(): Promise<Post[]> {
    return[...]. ; }}Copy the code

This method cannot get a Connection to the database and therefore cannot operate on the database.

To solve this problem, we can define an AppContext:

.type AppContext = {
  connection: Connection,
};

async function main() {...}

...
Copy the code

Note that import {… , Connection } from ‘typeorm’; .

Then, when creating the server, pass in a context parameter:

async function main() {

  try {

    ...

+ const context: AppContext = {
+ connection
+};

    const server = new ApolloServer({
+ context,schema, playground: true }); . } catch (error) { ... }}Copy the code

So our posts method can access the AppContext.

@Resolver(Post)
class PostResolver {

  @Query(returns= > [Post])
  async posts(
    @Ctx() context: AppContext,     // Dependencies can be obtained from AppContext) :Promise<Post[]> {
    constpostRepository = context.connection.getRepository(PostEntity); . }}Copy the code

Query the post

Find the following code:

@Resolver(Post)
class PostResolver {

  @Query(returns= > [Post])
  async posts(): Promise<Post[]> {
    return [...]
  }
}
Copy the code

Change the posts method to:


  @Query(returns= > [Post])
  async posts(
    @Ctx() context: AppContext,
  ): Promise<Post[]> {
    const postRepository = context.connection.getRepository(PostEntity);
    return await postRepository.find();
  }
Copy the code

Note that import {… , Ctx } from “type-graphql”; .

It returns all the blog posts in the database.

CTRL +C, $NPM start:

On Playground:

Data is empty because there is really no data in the database 😄.

Let’s try a new blog post.

The new post

Under the posts method, add createPost:

.asyncposts( ... ) :Promise<Post[]> { ... }
  
  @Mutation(returns= > Post)
  async createPost(
    @Ctx() context: AppContext,
    @Arg('content') content: string,
  ): Promise<Post> {
    const postRepository = context.connection.getRepository(PostEntity); 
    const post = await postRepository.save({ content });
    returnpost; }...Copy the code

It introduces a GraphQL “interface” for a new blog post:

Query and Mutation are both entries in GraphQL. Query is mainly responsible for read and Mutation is mainly responsible for write.

Mutation is a write operation, so Mutation is used.

CTRL +C, $NPM start:

Refresh the browser and test on Playground:

Inspection Results:

(We can also verify the results with MySQL Workbench, see how to use MySQL Workbench graphics tools to manipulate the database.)

Queries to Posts can now return new posts.

Modify the post

  @Mutation(returns= > Post)
  async updatePost(
    @Ctx() context: AppContext,
    @Arg('postId') postId: string.@Arg('content') content: string,
  ): Promise<Post> {
    const postRepository = context.connection.getRepository(PostEntity); 
    await postRepository.update(postId, { content });
    return await postRepository.findOneOrFail(postId);
  }
Copy the code

Restart service CTRL +C, $NPM start.

Refresh the browser and test on Playground:

The postId passed in here is returned when you query the blog post.

Inspection Results:

The post has been updated to “GraphQL”.

Delete the post

  @Mutation(returns= > String)
  async deletePost(
    @Ctx() context: AppContext,
    @Arg('postId') postId: string) :Promise<string> {
    const postRepository = context.connection.getRepository(PostEntity); 
    await postRepository.delete(postId);
    return postId;
  }
Copy the code

Restart service CTRL +C, $NPM start.

Refresh the browser and test on Playground:

Inspection Results:

The list of posts is empty, indicating that the posts you just created have been deleted.

The next step

This article describes how the GraphQL backend implements the add, delete, change and query function of the database. The following chapters will explain how Flutter introduces the “interface” created in this article and enables blog additions, queries, and other functions in the front-end.

The source code

The code address

src/index.ts

import "reflect-metadata"
import { buildSchema, ObjectType, Field, ID, Resolver, Query, Ctx, Arg, Mutation } from "type-graphql";
import { ApolloServer } from "apollo-server";
import { CreateDateColumn, Entity, PrimaryGeneratedColumn, Column, createConnection, Connection } from 'typeorm';

@Entity(a)class PostEntity {

  @PrimaryGeneratedColumn('uuid')
  id: string;

  @CreateDateColumn()
  created: Date;

  @Column('text')
  content: string;
}

@ObjectType(a)class Post {

  @Field(type= > ID)
  id: string;

  @Field()
  created: Date;

  @Field()
  content: string;
}

@Resolver(Post)
class PostResolver {

  @Query(returns= > [Post])
  async posts(
    @Ctx() context: AppContext,
  ): Promise<Post[]> {
    const postRepository = context.connection.getRepository(PostEntity);
    return await postRepository.find();
  }

  @Mutation(returns= > Post)
  async createPost(
    @Ctx() context: AppContext,
    @Arg('content') content: string,
  ): Promise<Post> {
    const postRepository = context.connection.getRepository(PostEntity); 
    const post = await postRepository.save({ content });
    return post;
  }

  @Mutation(returns= > Post)
  async updatePost(
    @Ctx() context: AppContext,
    @Arg('postId') postId: string.@Arg('content') content: string,
  ): Promise<Post> {
    const postRepository = context.connection.getRepository(PostEntity); 
    await postRepository.update(postId, { content });
    return await postRepository.findOneOrFail(postId);
  }

  @Mutation(returns= > String)
  async deletePost(
    @Ctx() context: AppContext,
    @Arg('postId') postId: string) :Promise<string> {
    const postRepository = context.connection.getRepository(PostEntity); 
    await postRepository.delete(postId);
    returnpostId; }}type AppContext = {
  connection: Connection,
};

async function main() {

  try {

    const schema = await buildSchema({
      resolvers: [PostResolver],
      dateScalarMode: 'timestamp'
    });

    const connection = await createConnection({
      type: 'mysql',
      host: 'localhost',
      port: 3306,
      username: 'tinylearn',
      password: 'my_password',
      database: 'tinylearn',
      synchronize: true,
      entities: [PostEntity],
    });

    const context: AppContext = {
      connection
    };

    const server = new ApolloServer({
      context,
      schema,
      playground: true
    });

    const { url } = await server.listen(4444);

    console.log(`GraphQL Playground available at ${url}`);
    
  } catch (error) {
    console.error(error);
  }
}


main();
Copy the code

reference

Best Practices for Full Stack GraphQL + Flutter Development, Article Series

How to Access a Database using TypeORM?

Docker start MySQL container

TypeORM

GraphQL

Flutter