How To Guide - rothfels/bespin GitHub Wiki

This page should help you get started implementing features. It is divided into two sections:

  • server: guide code running on the server (e.g. adding routes/endpoints, database models, migrations).
  • browser: guide for code running in the browser (e.g. React, GraphQL fetches)'

For debugging help, see Troubleshooting.

server

Adding an HTTP route/endpoint

In server.ts, add code like this:

server.express.get('/', (req, res) => {
  // your GET handler
})
server.express.post('/', (req, res) => {
  // your POST handler
})

Reading data data in an HTTP request, sending data in a response

In server.ts, use the req and res argument on your route handler:

server.express.get('/users/:userId', (req, res) => {
  const userId = req.params['userId']
  res.status(200).type('json').send({ id: 1, name: 'John' })
})

Adding a GraphQL resolver

Modify schema.graphql first to specify your resolver.

Re-run codegen to validate your schema and generate resolver types:

npm run gen

Then, add corresponding function resolvers in api.ts:

export const graphqlRoot: Resolvers<Context> = {
  Query: {
    self: (_, args, ctx) => ctx.user,
    surveys: () => Survey.find(),
  },
}

The generated types should validate that the code you write matches the schema.

Creating a database model

Your database tables contain rows, each can be represented by a TypeScript object. Using the TypeORM Object Relational Mapper, we can define a TypeScript class that will automatically generate a SQL table, provide query/mutation functions, etc.

First, add your model to the entities directory:

@Entity()
export class Person extends BaseEntity {
  @PrimaryGeneratedColumn()
  id: number

  @Column()
  name: string
}

Then, make sure to register the entity in sql.ts:

export async function initORM() {
  return await createConnection({
    ...
    entities: [User],
    ...
  })
}

Querying the database

Once you have created a database model (see previous section) you can use static methods on the class to fetch database rows:

const users = await User.find()

For complete options, see the TypeORM documentation.

Adding a database migration

TypeORM will try to automatically adjust the database schema to match your entity definitions.

If you need to seed/initialize your database with information, or want to manually run SQL statements, you can define a new migration file in the db/migrations folder. The file name should match the pattern VX.Y__Description.sql and specify an increased major or minor version from the previous migration run. So V1.1__ will run first, then V1.2__, etc.

If you get the database into an invalid state, see the Troubleshooting guide.

mysql database and redis cache

Starting the database and cache

To start the database and cache, please run the following command.

docker-compose up -d

This will spin up two docker containers, one for your mysql database and one for your redis cache.

Stopping the database and cache

To tear down the database and redis cache use the following command.

docker-compose down

Inspecting the database

For debugging purposes, it may be helpful to inspect contents of the mysql database. You can do so in two ways, either through a GUI tool or through the terminal. We highly recommend downloading a GUI tool as it is much easier to use.

GUI database tools

For Mac users we suggest using this test build of Sequel Pro. Two other options that should work for all users are Table Plus and MySQL Workbench, but MySQL Workbench is probably overkill for this project. Although any GUI database tool that is compatible with mysql8 should work. To connect to the database use the following settings.

Host: 127.0.0.1
Username: root
Password: password
Database: team_slug (or bespin if you forgot to change this!) 
Port: 3307

Using terminal

If you don't want to download any additional software or have installations issues, you can always connect through terminal, although it will lack fun buttons to push. To find the name of your container, run

docker ps

The name of your container should take the form [team-slug]_db_1. To enter inside the docker container and run mysql through terminal, run the following command replacing my_sql_container_name appropriately.

docker exec -it my_sql_container_name mysql -uroot -p

When prompted enter the password password. Then enter this series of sql commands where database_name and table_name are the names of whatever database and table you wish to inspect. The database_name is most likely your team_slug or bespin if you forgot to change it to your team slug.

USE database_name
show tables;
SELECT * FROM table_name

browser

Writing a React component

The simplest React component is just a function that takes a JSON argument (properties, or props) and returns JSX (HTML):

function MyComponent(props: { title: string }) {
  return <div>{props.title}</div>
}

It's possible for your component to take no arguments at all!

function MyComponent() {
  return <div>hello world</div>
}

Styling a React component

The easiest way to style components is to load a CSS library:

<link href="https://unpkg.com/tailwindcss@^1.0/dist/tailwind.min.css" rel="stylesheet">

Then you can attach className to your React components:

function MyComponent() {
  return <button className="btn btn-large">click me</button>
}

Use a helper function to create components with className automatically attached:

const MyStyledButton = style('button', 'btn btn-large') // now you can use <MyStyledButton> instead of <button className="btn btn-large">

You can also attach inline styles:

const MyStyledButton = style('button', 'btn btn-large', {
  ':hover': {
    backgroundColor: '#555',
  },
})

Adding state to your component

Your component will re-render if it's passed different props, but you can also change how it's rendered by storing internal state in the component:

function MyStatefulComponent() {
  const [count, setCount] = useState(0) // count initialized to 0
  return <button onClick={() => setCount(count + 1)}>{count}</button>
}

Adding a GraphQL query to your component

Your component can ask the server for data using a GraphQL query:

function MyQueryComponent() {
  const { loading, data } = useQuery<MyQuery>(myQuery)
  if (loading) {
    return <div>loading state</div>
  }
  if (data == null) {
    return <div>no data!</div>
  }
  return <div>{data}</div>
}

In the code above, myQuery is defined like this:

const myQuery = gql`
  query MyQuery {
    field {
      subField
    }
  }
`

Once we define myQuery, we run codegen to generate the MyQuery type:

npm run gen
⚠️ **GitHub.com Fallback** ⚠️