Refactor database setup and add user context to GQL requests (#17)

* Add context to each request

* Implement get user db query

* Move constants to a separate file

* Refactor database init method
This commit is contained in:
neerajvijay1997 2024-01-17 10:53:01 +05:30 committed by Ashwin Phatak
parent 4d00cfb8f0
commit e4c099f8c3
7 changed files with 63 additions and 28 deletions

View File

@ -2,3 +2,6 @@
host = "127.0.0.1" host = "127.0.0.1"
port = 8000 port = 8000
gqlPath = "/graphql" gqlPath = "/graphql"
[database]
dbPath = "db/snowball"

View File

@ -4,6 +4,11 @@ export interface ServerConfig {
gqlPath?: string; gqlPath?: string;
} }
export interface DatabaseConfig {
dbPath: string;
}
export interface Config { export interface Config {
server: ServerConfig; server: ServerConfig;
database: DatabaseConfig;
} }

View File

@ -0,0 +1,4 @@
export const DEFAULT_GQL_PATH = '/graphql';
// Note: temporary hardcoded user, later to be derived from auth token
export const USER_ID = 1;

View File

@ -2,23 +2,35 @@ import { DataSource } from 'typeorm';
import path from 'path'; import path from 'path';
import debug from 'debug'; import debug from 'debug';
const log = debug('snowball:server'); import { User } from './entity/User';
import { DatabaseConfig } from './config';
export const initializeDatabase = async ( const log = debug('snowball:database');
database: string = 'db/snowball'
): Promise<void> => { export class Database {
try { private dataSource: DataSource;
const AppDataSource = new DataSource({
constructor ({ dbPath }: DatabaseConfig) {
this.dataSource = new DataSource({
type: 'better-sqlite3', type: 'better-sqlite3',
database, database: dbPath,
entities: [path.join(__dirname, '/entity/*')], entities: [path.join(__dirname, '/entity/*')],
synchronize: true, synchronize: true,
logging: false logging: false
}); });
await AppDataSource.initialize();
log('database initialized');
} catch (error) {
log(error);
} }
};
async init () : Promise<void> {
await this.dataSource.initialize();
log('database initialized');
}
async getUser (userId: number) : Promise<User | null> {
const userRepository = this.dataSource.getRepository(User);
const user = await userRepository.findOneBy({
id: userId
});
return user;
}
}

View File

@ -3,22 +3,26 @@ import debug from 'debug';
import fs from 'fs'; import fs from 'fs';
import path from 'path'; import path from 'path';
import { initializeDatabase } from './database'; import { Database } from './database';
import { createAndStartServer } from './server'; import { createAndStartServer } from './server';
import { createResolvers } from './resolvers'; import { createResolvers } from './resolvers';
import { getConfig } from './utils'; import { getConfig } from './utils';
import { Config } from './type'; import { Config } from './config';
const log = debug('snowball:server'); const log = debug('snowball:server');
const configFilePath = 'environments/local.toml'; const configFilePath = 'environments/local.toml';
export const main = async (): Promise<void> => { export const main = async (): Promise<void> => {
// TODO: get config path using cli // TODO: get config path using cli
const { server } = await getConfig<Config>(configFilePath); const { server, database } = await getConfig<Config>(configFilePath);
const db = new Database(database);
await db.init();
await initializeDatabase();
const typeDefs = fs.readFileSync(path.join(__dirname, 'schema.gql')).toString(); const typeDefs = fs.readFileSync(path.join(__dirname, 'schema.gql')).toString();
await createAndStartServer(typeDefs, createResolvers, server); const resolvers = await createResolvers(db);
await createAndStartServer(typeDefs, resolvers, server);
}; };
main() main()

View File

@ -1,12 +1,16 @@
const user = { import { Database } from './database';
id: 2
};
export const createResolvers = async (): Promise<any> => { export const createResolvers = async (db: Database): Promise<any> => {
return { return {
Query: { Query: {
// TODO: fetch user data from db user: (
user: () => user _: any,
__: any,
// TODO: add custom type for context
context: any
) => {
return db.getUser(context.userId);
}
} }
}; };
}; };

View File

@ -10,12 +10,11 @@ import {
import { TypeSource } from '@graphql-tools/utils'; import { TypeSource } from '@graphql-tools/utils';
import { makeExecutableSchema } from '@graphql-tools/schema'; import { makeExecutableSchema } from '@graphql-tools/schema';
import { ServerConfig } from './type'; import { ServerConfig } from './config';
import { DEFAULT_GQL_PATH, USER_ID } from './constants';
const log = debug('snowball:server'); const log = debug('snowball:server');
const DEFAULT_GQL_PATH = '/graphql';
export const createAndStartServer = async ( export const createAndStartServer = async (
typeDefs: TypeSource, typeDefs: TypeSource,
resolvers: any, resolvers: any,
@ -31,12 +30,16 @@ export const createAndStartServer = async (
// Create the schema // Create the schema
const schema = makeExecutableSchema({ const schema = makeExecutableSchema({
typeDefs, typeDefs,
resolvers: await resolvers() resolvers
}); });
const server = new ApolloServer({ const server = new ApolloServer({
schema, schema,
csrfPrevention: true, csrfPrevention: true,
context: () => {
// TODO: Use userId derived from auth token
return { userId: USER_ID };
},
plugins: [ plugins: [
// Proper shutdown for the HTTP server // Proper shutdown for the HTTP server
ApolloServerPluginDrainHttpServer({ httpServer }), ApolloServerPluginDrainHttpServer({ httpServer }),