Skip to content

Commit a0ae33c

Browse files
committed
Move deno demo GraphQL server into project.
1 parent 49cd38f commit a0ae33c

File tree

11 files changed

+832
-0
lines changed

11 files changed

+832
-0
lines changed

server/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
.env

server/Data/Db.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { config } from 'dotenv.ts';
2+
import {
3+
MongoClient,
4+
} from "mongo.ts";
5+
6+
7+
const { parsed: env } = config();
8+
9+
if (typeof env === 'undefined') throw new Error('No .env file found');
10+
11+
const client = new MongoClient();
12+
export const db = await client.connect(env.MONGODB_URI);

server/Post/Post.ts

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import { db } from "../Data/Db.ts";
2+
import { ObjectId } from "mongo.ts";
3+
4+
type PostRecord = {
5+
_id?: ObjectId;
6+
title: string;
7+
body: string;
8+
published?: boolean;
9+
};
10+
11+
export class Post {
12+
constructor({ _id, title, body, published = true }: PostRecord) {
13+
this.id = _id;
14+
this.title = title;
15+
this.body = body;
16+
this.published = published;
17+
}
18+
19+
id?: ObjectId;
20+
title: string;
21+
body: string;
22+
published?: boolean;
23+
24+
static collection = db.collection("Post");
25+
26+
async save() {
27+
let rec;
28+
if (this.id) {
29+
rec = await Post.collection.updateOne({ _id: this.id }, { $set: this });
30+
} else {
31+
rec = await Post.collection.insertOne(this);
32+
}
33+
34+
const saved = new Post({ ...this, ...rec });
35+
Object.assign(this, saved);
36+
return saved;
37+
}
38+
39+
static async find(query?: any) {
40+
const records = await Post.collection.find(query).toArray();
41+
return records.map((record) => new Post(record as PostRecord));
42+
}
43+
44+
static async load(_id: string) {
45+
const record = await Post.collection.findOne({ _id: new ObjectId(_id) });
46+
if (typeof record == "undefined") return null;
47+
return new Post(record as PostRecord);
48+
}
49+
}

server/User/User.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import { db } from "../Data/Db.ts";
2+
import { ObjectId } from "mongo.ts";
3+
4+
type UserRecord = {
5+
_id?: ObjectId;
6+
name: string;
7+
email: string;
8+
age?: number;
9+
};
10+
11+
export class User {
12+
constructor({ _id, name, email, age }: UserRecord) {
13+
this.id = _id;
14+
this.name = name;
15+
this.email = email;
16+
this.age = age;
17+
}
18+
19+
id?: ObjectId;
20+
name: string;
21+
email: string;
22+
age?: number;
23+
24+
static collection = db.collection("User");
25+
26+
async save() {
27+
let rec;
28+
if (this.id) {
29+
rec = await User.collection.updateOne({ _id: this.id }, { $set: this });
30+
} else {
31+
rec = await User.collection.insertOne(this);
32+
}
33+
34+
const saved = new User({ ...this, ...rec });
35+
Object.assign(this, saved);
36+
return saved;
37+
}
38+
39+
static async find(query?: any) {
40+
const records = await User.collection.find(query).toArray();
41+
return records.map((record) => new User(record as UserRecord));
42+
}
43+
44+
static async load(_id: string) {
45+
const record = await User.collection.findOne({ _id: new ObjectId(_id) });
46+
if (typeof record == "undefined") return null;
47+
return new User(record as UserRecord);
48+
}
49+
50+
static async delete(_id: string) {
51+
return await User.collection.deleteOne({ _id: new ObjectId(_id) });
52+
}
53+
}

server/app.ts

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
import { Application, Router } from 'oak.ts';
2+
import { applyGraphQL, gql, GQLError } from 'oak_graphql.ts';
3+
import { User } from './User/User.ts';
4+
import { Post } from './Post/Post.ts';
5+
import _ from 'underscore.ts';
6+
7+
const app = new Application();
8+
9+
app.use(async (ctx, next) => {
10+
await next();
11+
const rt = ctx.response.headers.get('X-Response-Time');
12+
console.log(`${ctx.request.method} ${ctx.request.url} - ${rt}`);
13+
});
14+
15+
app.use(async (ctx, next) => {
16+
const start = Date.now();
17+
await next();
18+
const ms = Date.now() - start;
19+
ctx.response.headers.set('X-Response-Time', `${ms}ms`);
20+
});
21+
22+
const typeDefs = gql`
23+
input CreateUserInput {
24+
name: String!
25+
email: String!
26+
age: Int
27+
}
28+
29+
type User {
30+
id: ID!
31+
name: String!
32+
email: String!
33+
age: Int
34+
}
35+
36+
type Post {
37+
id: ID!
38+
title: String!
39+
body: String!
40+
published: Boolean!
41+
author: User
42+
}
43+
44+
type SuccessOrError {
45+
success: Boolean!
46+
message: String
47+
}
48+
49+
type Query {
50+
testInput(input: CreateUserInput!): User
51+
me: User!
52+
user(id: ID!): User
53+
users: [User]!
54+
post(id: ID!): Post
55+
posts(query: String): [Post!]!
56+
grades: [Int!]!
57+
add(nums: [Int!]!): Int!
58+
}
59+
60+
type Mutation {
61+
createUser(name: String!, email: String!, age: Int): User!
62+
deleteUser(id: ID!): SuccessOrError!
63+
createPost(title: String!, body: String!, published: Boolean!): Post!
64+
}
65+
`;
66+
67+
// console.log(JSON.stringify(typeDefs, null, 2));
68+
69+
const resolvers = {
70+
Query: {
71+
testInput: async (
72+
parent: any,
73+
{ input }: { input: { name: string; email: string; age?: number } },
74+
context: any,
75+
info: any
76+
) => {
77+
return { id: 'wtf', ...input };
78+
},
79+
grades: async (parent: any, args: any, context: any, info: any) => {
80+
return [1, 2, 3, 4, 5];
81+
},
82+
add: async (
83+
parent: any,
84+
{ nums }: { nums: number[] },
85+
context: any,
86+
info: any
87+
) => {
88+
return nums.reduce((a, b) => a + b, 0);
89+
},
90+
me: async (parent: any, args: any, context: any, info: any) => {
91+
return {
92+
id: 'jdillick',
93+
name: 'John',
94+
email: 'john@dillick.us',
95+
};
96+
},
97+
98+
user: async (parent: any, { id }: any, context: any, info: any) => {
99+
const user = await User.load(id);
100+
return user;
101+
},
102+
103+
users: async (parent: any, args: any, context: any, info: any) => {
104+
// await new Promise((resolve) => setTimeout(resolve, 5000));
105+
const users = User.find();
106+
return users;
107+
},
108+
109+
post: async (
110+
parent: any,
111+
{ id }: { id: number },
112+
context: any,
113+
info: any
114+
) => {
115+
console.log({ id });
116+
return {
117+
id: 123,
118+
title: 'Hello World',
119+
body: 'This is a post.',
120+
published: true,
121+
};
122+
},
123+
124+
posts: async (parent: any, { query }: any, context: any, info: any) => {
125+
if (query) {
126+
return Post.find({ title: { $regex: query, $options: 'i' } });
127+
}
128+
return Post.find();
129+
},
130+
},
131+
Mutation: {
132+
createUser: async (
133+
parent: any,
134+
{ name, email, age }: { name: string; email: string; age?: number },
135+
context: any,
136+
info: any
137+
) => {
138+
const user = new User({ name, email, age });
139+
await user.save();
140+
return user;
141+
},
142+
deleteUser: async (
143+
parent: any,
144+
{ id }: { id: string },
145+
context: any,
146+
info: any
147+
) => {
148+
try {
149+
const user = await User.delete(id);
150+
return { success: true };
151+
} catch (e) {
152+
return { success: false, message: e.message };
153+
}
154+
},
155+
createPost: async (
156+
parent: any,
157+
{
158+
title,
159+
body,
160+
published,
161+
}: {
162+
title: string;
163+
body: string;
164+
published: boolean;
165+
},
166+
context: any,
167+
info: any
168+
) => {
169+
const post = new Post({ title, body, published });
170+
await post.save();
171+
return post;
172+
},
173+
},
174+
};
175+
176+
const GraphQLService = await applyGraphQL<Router>({
177+
Router,
178+
typeDefs,
179+
resolvers,
180+
context: (ctx) => {
181+
// this line is for passing a user context for the auth
182+
return { user: 'Boo' };
183+
},
184+
});
185+
186+
app.use(GraphQLService.routes(), GraphQLService.allowedMethods());
187+
188+
console.log('Server start at http://localhost:4000');
189+
await app.listen({ port: 4000 });

server/db/.gitkeep

Whitespace-only changes.

server/db/posts.json.db

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
{"title":"Hello World","body":"This is a new post.","published":true,"_id":"d138638a-90d0-4b24-b4fc-fb0bba78e075"}
2+
{"title":"My first post","body":"I'm writing to say: graphql is pretty cool.","published":true,"_id":"e580cd90-5f6c-4771-ac2c-dde327ce0908"}

server/db/users.json.db

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{"name":"john","email":"john@dillick.us","_id":"f3ddef83-0534-4f0a-b334-8128140477e7"}
2+
{"name":"john","email":"john@dillick.us","_id":"bd972d6f-898d-421a-91d0-a28d3229d817"}
3+
{"name":"john","email":"john@dillick.us","_id":"b6a6c5ab-3232-4cf4-9b1f-a04abf025f68"}

server/deno.json

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
{
2+
"unstable": [
3+
"bare-node-builtins",
4+
"byonm",
5+
"unsafe-proto",
6+
"webgpu",
7+
"broadcast-channel",
8+
"worker-options",
9+
"cron",
10+
"kv",
11+
"ffi",
12+
"fs",
13+
"net"
14+
],
15+
"imports": {
16+
"/": "./",
17+
"./": "./",
18+
"archisaurus.ts": "https://deno.land/x/archisaurus@v0.0.1/mod.ts",
19+
"tilia/": "https://deno.land/x/tilia@0.1.2/src/",
20+
"oak.ts": "https://deno.land/x/oak@v10.0.0/mod.ts",
21+
"oak_graphql.ts": "https://deno.land/x/oak_graphql/mod.ts",
22+
"mongoose.ts": "https://deno.land/x/mongoose@8.5.2/mod.ts",
23+
"reactium-core-sdk.ts": "https://esm.sh/@atomic-reactor/reactium-sdk-core@2.2.0/core?bundle-deps",
24+
"underscore.ts": "https://esm.sh/underscore@1.12.1",
25+
"mongo.ts": "https://deno.land/x/mongo@v0.33.0/mod.ts",
26+
"dotenv.ts": "https://deno.land/x/dot_env@0.2.0/mod.ts"
27+
},
28+
"lint": {
29+
"rules": {
30+
"tags": ["recommended"],
31+
"rules": {
32+
"no-explicit-any": false
33+
}
34+
}
35+
}
36+
}

0 commit comments

Comments
 (0)