Hardcoded Data Setup
The easiest way to get started with Pylon is to use in-memory (hardcoded) data. For this, we will create a simple class that holds the data and performs all the necessary operations on it.
Project Structure
After setting up Pylon correctly, you should see a project structure similar to this:
my-pylon/
├── .pylon/
├── src/
│ ├── index.ts
├── package.json
├── tsconfig.json
The directory we are interested in is the src/
directory. This is where we will create our class BookingStore
that holds the static data.
Info
Pylon doesn't restrict you in any way when it comes to implementing your logic. You can use classes, functions, or any other way you like.
Create a Data Structure
Since Pylon let's us freely implement our logic, independent of the structure and its location, we can simply create a new file bookingStore.ts
directly in the src/
directory:
my-pylon/
├── .pylon/
├── src/
│ ├── bookingStore.ts
│ ├── index.ts
├── package.json
├── tsconfig.json
Before we start implementing the class, it's a good idea to think about what our data structure should look like.
Therefore, we will create a simple interface Booking
that represents a booking:
interface Booking {
id: number
firstname: string
lastname: string
totalPrice?: number
depositpaid: boolean
bookingdates: {
checkin: string
checkout: string
}
additionalneeds?: string
}
Each Booking
object represents a booking of a guest, including the guest's name, the total price, if a deposit was paid, the booking dates, and additional needs (e.g. Breakfast).
Implement the Class
Now that we have defined our data structure, we can start implementing the BookingStore
class. This class will hold the static data and provide methods to interact with it.
class BookingStore {
private bookings: Booking[] = [
{
id: 1,
firstname: 'Sally',
lastname: 'Brown',
totalPrice: 111,
depositpaid: true,
bookingdates: {
checkin: '2013-02-23',
checkout: '2014-10-23'
},
additionalneeds: 'Breakfast'
},
{
id: 2,
firstname: 'John',
lastname: 'Doe',
totalPrice: 222,
depositpaid: false,
bookingdates: {
checkin: '2013-02-23',
checkout: '2014-10-23'
},
additionalneeds: 'Breakfast'
},
{
id: 3,
firstname: 'Jane',
lastname: 'Smith',
totalPrice: 333,
depositpaid: true,
bookingdates: {
checkin: '2013-02-23',
checkout: '2014-10-23'
},
additionalneeds: 'Breakfast'
}
]
}
Currently, the class only holds the data. In the next step, we will add methods to interact with the data.
Add Methods to Interact with the Data
To interact with the data, we will add methods to the BookingStore
class. For this example, we will implement two methods: getBookings
and getBookingById
.
getBookings(): Booking[] {
return this.bookings;
}
getBookingById(bookingId: number): Booking | null {
return this.bookings.find((booking) => booking.id === bookingId) ?? null;
}
Note that we accept a bookingId
as an argument for the getBookingById
method. Pylon recognizes this argument, provides it in the GraphQL interface, and reliably passes it to the method when it is called. This allows us to fetch a specific booking based on the provided ID.
Info
Defining arguments in the method signature will be automatically added to the GraphQL schema and can be used in the respective queries or mutations. Pylon takes care of passing the arguments to the method when it is called and returning the correct data. We only need to assure that enough type safety is given.
Bind this
to the Class Methods
Great! We are missing one last step inside our class implementation: Binding this
to the class methods so that we can access the class properties inside the methods.
constructor() {
this.getBookings = this.getBookings.bind(this)
this.getBookingById = this.getBookingById.bind(this)
}
Now, we can use the BookingStore
class to get all bookings or a specific booking by its ID. However, we still need to tell Pylon that we want to use this class in our service.
Important
Don't forget to bind this
to any of the class methods that you want to use
in your service. Otherwise, you will get a runtime error when calling a class
method that uses this
.
Register the Class Methods in Pylon
Though we have implemented the BookingStore
class, Pylon doesn't know about it yet. To make it available in our service, we first need to export an instance from our newly created class. Since we need to access the same instance for every request, we will export a singleton instance.
const bookingStore = new BookingStore()
export default bookingStore
Now, the only thing left to do is to notify Pylon about our new class methods. For this, the index.ts
file is the place to go. Currently, the file should look like this:
import {app} from '@getcronit/pylon'
export const graphql = {
Query: {
hello: () => 'Hello World'
},
Mutation: {}
}
export default app
Since we dont need the hello
resolver anymore, we can remove it and import the bookingStore
instance. We can then add the getBookings
and getBookingById
methods to the Query
object:
import {app} from '@getcronit/pylon'
import bookingStore from './lib/bookingStore'
export const graphql = {
Query: {
bookings: bookingStore.getBookings,
booking: bookingStore.getBookingById
},
Mutation: {}
}
export default app
The property names bookings
and booking
are the names of the queries that we can use in the GraphQL interface. The values are the methods that we want to call when the queries are executed.
And that's it! We have successfully implemented a simple class that holds hardcoded data and provides methods to interact with it. To test the implementation, Pylon provides a built-in GraphQL playground where we can execute our new queries after running bun dev
:
How Does It Work?
When we start the service, Pylon automatically creates a GraphQL schema based on the methods we registered in the graphql
object. This schema is then used to validate incoming queries and mutations. When a query or mutation is executed, Pylon calls the corresponding method and returns the result to the client.
You can think of Pylon as a kind of “mapper” with regard to GraphQL. When we introduce new functions to Pylon, it checks the signature of the function and builds a GraphQL schema from the TypeScript types. This automatically converts our functions into GraphQL endpoints. This means that we don't have to write GraphQL schemas manually, Pylon does it for us and we can just focus on implementing the functions.
Furthermore, Pylon does not care how we write and build our functions. We can rely on complex classes, whose methods we then introduce into Pylon, or we can rely on simple functions, which we then include in Pylon. We can also rely on a mixture of both. Pylon is very flexible in this respect and allows us to find the solution that suits us best. Pylon only expects us to annotate our code type so that Pylon can generate the correct GraphQL schema.
Modify the Data
Modifying the data works the same way as reading it. Let's say we want to add a new booking to our data store. We can simply add a new method to the BookingStore
class:
addBooking(booking: Omit<Booking, "id">): Booking {
const newBooking = { ...booking, id: this.bookings.length + 1 };
this.bookings.push(newBooking);
return newBooking;
}
This method is pretty straightforward. It takes a new booking object, assigns a new ID to it, and adds it to the bookings
array. Since we dont want the client to set the ID, we use Omit<Booking, "id">
to exclude the id
property from the booking
object. You can of course put each type field into a separate parameter if you prefer. We use this approach to keep the method signature simple.
To make the new method available in our service, we need to add it to the index.ts
file (dont forget to bind this
to the method too):
export const graphql = {
Query: {
bookings: bookingStore.getBookings,
booking: bookingStore.getBookingById
},
Mutation: {
addBooking: bookingStore.addBooking
}
}
Since this method changes our data, we place it in the Mutation
object. Now, we can start using this new mutation. To test it, we can use the built-in GraphQL playground:
Perfect! We have successfully implemented a simple class that holds in-memory data and provides methods to interact with it.
You can go ahead and implement more methods to interact with the data, like updating or deleting a booking. The possibilities are endless, and Pylon gives you the freedom to implement your logic the way you like it.
Conclusion
This guide showed how easy it is to get started with Pylon. We created a simple class that holds hardcoded data and provides methods to interact with it. We then registered the class methods in Pylon and tested them using the built-in GraphQL playground.
In the next guide, we will take a look at how we can use external data sources to fetch data in our service.