FANDOM (DnD Beyond) web project
I decided to create a fully-separated architecture with a frontend being deployed/developed separately from the backend/REST API. This would allow us to have multiple clients interface (in the case we’re developing mobile applications or smart devices or anything else that needs the API) with the API since there is no coupling involved between the backend and the consumer.
The frontend is written using React with TypeScript; nothing too crazy if you’ve used React before. I’m using hooks as I feel they’re the best way to write resilient components and avoid introducing some development practices that are hard to debug. The only meaningful libraries pulled in are React Router for client-side routing and styled-components for CSS-in-JS styling. I’m using Cloudinary as a CDN to host the party logos and character avatars, which is admittedly a bit overkill for this project.
The backend is also a vanilla .NET Core REST API scaffolded using the dotnet
CLI. I decided to use Entity Framework Core’s InMemoryProvider
to store information to meet the requirements for storing the data in memory.
Prerequisites
Clone Repo
git clone https://github.com/ByronGuina/direwolves-and-dragons
Frontend
npm install # yarn
npm start # yarn start
Backend
cd backend
dotnet run
You don’t need to run any migration scripts for seeding data as I’m using Entity Framework Core’s InMemory Provider and initializing the data when the application starts.
See backend/Data/DataGenerator.cs
The application frontend is hosted at https://direwolves-and-dragons.netlify.com and the backend/API is hosted on Digital Ocean. Unfortunately, it’s read-only. For whatever reason the POST, PATCH, and DELETE methods are getting blocked by CORS, even though I have CORS on the backend set up to allow any origin, method, and header. I’m looking into this as I have time, so please think of the deployment as a nice icing on the cake if it starts working.
Synchronizing the types between C# and TypeScript caused some difficulty, particularly with how differently C# and TypeScript handle values for enums. Rather than set up complicated validation logic on the client and server, I just opted to store the race and class as strings and let the user enter whatever they want. In the future I’d rather restrict the race and class options and unify the types between the client and API.
Now-a-days I tend to prefer to use GraphQL over than REST-based APIs where possible. Since the GraphQL schema is typed and inspect-able, we get some really convenient tooling for generating client-side types based on our backend schema.
Creating the initial 25+ characters with associated classes, races, stats, and images took longer than I expected. Thank you Twitch for giving me something to watch in the background while changing 500 lines one at a time.
I’m not a designer (although that’s an area I’m improving). Given the short time-frame for the project the UI isn’t as fleshed out, designed, or consistent as it could be. Next steps would be to unify the UI components a bit and make the applications have a better overall feel.
Additionally, it’s not mobile-friendly at all. Depending on the target user-base, creating the application to be mobile-first could have been a higher priority.
The management forms for a given item are missing a few key features usually found in forms. There isn’t super strict validation for every field in the form. The number-related statistics and saving throws fields do have validation to ensure the value entered is a number, but the pure string fields do not. Including validation and error states for the user-entered fields could help direct and educate users to provide a better user experience. Additionally, there is no sanitation for the fields on either the client or the backend. Next steps would be to validate the fields to ensure data integrity and a better user experience while also sanitizing inputs on the client and backend to protect against malicious scripts.
Another optimization win would be to implement image optimization for better performance and correctly size the images for the avatars and party logos. Since we’re using Cloudinary as our image CDN we can achieve this fairly easily for the initial data with some url parameters. Additionally I’d use better/more professional pictures altogether.
Along with this, I would add functionality for uploading images rather than links, which would be stored in Cloudinary. This would allow us to use all the image optimization and caching wins we get with a CDN for every image rather than just the initial ones.