Welcome to this series where we will build a complete Catalog GraphQL API using InversifyJS. In this first part, we will set up our project structure, configure TypeScript, and establish a build flow that automatically generates TypeScript types from our GraphQL schemas.
Overview
We are going to build a backend service that allows managing products and categories. We will use:
- Node.js & TypeScript as our runtime and language.
- InversifyJS for Dependency Injection.
- GraphQL for our API query language.
- Apollo Server to serve our GraphQL API.
- Prisma (in later parts) for database access.
You can access the final implementation in our framework examples monorepo.
Step 1: Project Initialization
First, let's create a new directory for our project and initialize it. We will be setting this up as a standalone repository (not a monorepo).
mkdir catalog-graphql-api
cd catalog-graphql-api
pnpm init
Now, let's install the necessary dependencies.
Runtime Dependencies
pnpm add inversify reflect-metadata graphql @apollo/server express @inversifyjs/http-express @inversifyjs/apollo-express @inversifyjs/http-core @inversifyjs/apollo-core dotenv
Development Dependencies
We need TypeScript and some tools for our build process.
pnpm add -D typescript @types/node ts-node @inversifyjs/graphql-codegen rimraf prettier
Configure ESM
We will be using ECMAScript Modules (ESM) for this project. Open your package.json and ensure you have "type": "module" set. This is important for top-level await support in our scripts.
{
"name": "catalog-graphql-api",
"version": "1.0.0",
"type": "module",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
}
}
Step 2: TypeScript Configuration
Create a tsconfig.json file in the root of your project. This configures how TypeScript compiles our code.
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"lib": ["ES2022"],
"outDir": "./lib",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src"]
}
Note:
experimentalDecoratorsandemitDecoratorMetadataare crucial for InversifyJS to work correctly.
Step 3: Defining GraphQL Schemas
We will define our GraphQL schemas in .graphql files. This allows us to keep our schema definitions clean and separate from our code.
Create a directory structure graphql/schemas:
mkdir -p graphql/schemas
Let's create a common schema file graphql/schemas/common.graphql for shared types:
type PageInfo {
hasNextPage: Boolean!
hasPreviousPage: Boolean!
startCursor: String
endCursor: String
}
interface Node {
id: ID!
}
And a main schema file graphql/schemas/schema.graphql. We'll add a simple query to start with:
# import * from "common.graphql"
type Query {
hello: String
}
Step 4: Generating TypeScript Types
One of the best practices in GraphQL development is to generate TypeScript types from your schema. This ensures that your resolvers are type-safe and match your schema definition.
We will use @inversifyjs/graphql-codegen for this. Create a script file at src/scripts/generateGraphqlTypes.ts.
import { generateTsModels } from '@inversifyjs/graphql-codegen';
import * as prettier from 'prettier';
// Basic prettier config
const prettierConfig: prettier.Options = {
parser: 'typescript',
singleQuote: true,
trailingComma: 'all',
};
await generateTsModels({
destinationPath: './src/graphql/models/types.ts',
pluginConfig: {
avoidOptionals: false,
enumsAsTypes: true,
resolverTypeWrapperSignature: 'Partial<T> | Promise<Partial<T>>',
useIndexSignature: false,
},
prettierConfig,
schemas: {
glob: {
patterns: ['./graphql/schemas/*.graphql'],
},
},
});
This script does the following:
- Reads all
.graphqlfiles ingraphql/schemas. - Generates TypeScript interfaces and types.
- Saves them to
src/graphql/models/types.ts.
Step 5: Configuring Build Scripts
Now, let's add some scripts to our package.json to run the generation and build the project.
Update the scripts section in package.json:
"scripts": {
"build": "tsc && pnpm run generate:graphql:types",
"build:clean": "rimraf lib",
"generate:graphql:types": "ts-node ./src/scripts/generateGraphqlTypes.ts",
"prebuild": "pnpm run build:clean"
}
We use ts-node to run our generation script directly without compiling it first.
Step 6: Running the Build
Now you can run the build command:
pnpm run build
If everything is set up correctly, you should see a new file generated at src/graphql/models/types.ts containing the TypeScript definitions for your GraphQL schema.
Conclusion
We have successfully set up our project structure and a build pipeline that ensures our TypeScript types are always in sync with our GraphQL schema.
In the next part, we will implement the InversifyJS container, set up the Apollo Server, and create our first resolvers.
Top comments (0)