Demonstration bridge between ATproto and GraphQL. Generate schema types and interface with the ATmosphere via GraphQL queries. Includes a TypeScript server with IDE.
1import express from "express";
2import { createHandler } from "graphql-http/lib/use/http";
3import { buildSchema } from "graphql";
4import { ruruHTML } from "ruru/server";
5import { serveStatic } from "ruru/static";
6import { generateDefinitions } from "../generateLexiconSchema";
7
8// Initialize Express app
9const app = express();
10
11const lexiconIds = (await import("../../lexicons.json")).lexicons;
12
13// Generate definitions (this is what main.ts does when no --output flag is provided)
14const { chunks: generatedDefinitions, lexiconCalls } =
15 generateDefinitions(lexiconIds);
16
17// Define GraphQL schema using GraphQL-JS
18const schema = buildSchema(
19 generatedDefinitions.join("\n\n") +
20 `
21type Query {
22 lexicon: Lexicon
23}`,
24);
25
26const ty = (schema.getQueryType().getFields()["lexicon"].resolve = () => ({}));
27
28// Serve static files
29for (const key of Object.keys(lexiconCalls)) {
30 const segments = key.split(".");
31 const ty = schema.getType(
32 ["Lexicon"].concat(segments.slice(0, -1)).join("_"),
33 );
34 ty.getFields()[segments.slice(-1)[0]].resolve = lexiconCalls[key];
35
36 for (let i = 1; i < segments.length; i++) {
37 const ty = schema.getType(
38 ["Lexicon"].concat(segments.slice(0, i - 1)).join("_"),
39 );
40 ty.getFields()[segments.slice(0, i).pop()].resolve = () => ({});
41 }
42}
43
44app.use(express.static("public"));
45
46// Serve lexicons.json at /lexicons path
47app.get("/lexicons", (req, res) => {
48 res.json(Object.keys(lexiconCalls));
49});
50
51const handler = createHandler({ schema });
52
53// GraphQL endpoint
54app.all("/graphql", handler);
55
56/**
57 * Setup GraphiQL
58 */
59
60const config = { staticPath: "/ruru-static/", endpoint: "/graphql" };
61
62// Serve Ruru HTML
63app.get("/graphiql", (req, res) => {
64 res.format({
65 html: () => res.status(200).send(ruruHTML(config)),
66 default: () => res.status(406).send("Not Acceptable"),
67 });
68});
69
70// Serve static files
71app.use(serveStatic(config.staticPath));
72
73// Start the HTTP server
74const PORT = process.env.PORT || 4000;
75const httpServer = app.listen(PORT, () => {
76 console.log(`Server ready at http://localhost:${PORT}`);
77 console.log(`GraphQL endpoint: http://localhost:${PORT}/graphql`);
78 console.log(`GraphiQL endpoint: http://localhost:${PORT}/graphiql`);
79});
80
81// Handle server shutdown gracefully
82process.on("SIGINT", () => {
83 httpServer.close(() => {
84 console.log("Server closed");
85 });
86});