Lot's has changed in a few weeks and I've learned a bunch about a bunch of APIs!
I wanted to provide businesses and individuals the infrastructure to help those most in need. Importantly, I wanted to ensure that I did it in a way that kept things local and helps to grow bonds/relationships in the community.
What it does now?
It can be explained as a general purpose, anyone can join deliveroo.
- Passwordless Auth (Twilio/Auth0)
- Post a job
- Automatically notify others in your area (Twilio/Sendgrid)
- Manage job lifecycle
- Communicate with others (Twilio)
- Receive payment (Stripe)
- Two-way feedback
What's next?
- Brand - The gorgeous material-ui has made it straight forward to make something that looks good, but I need to spend some time creating a feeling and identity etc.
- Feature/groups - Some early feedback was that it would be able to create private groups to add an extra layer of trust.
- Population density based search area - using open data, I'd like to set default search area based on population density. Somone living out in the sticks will benefit from a larger area, whereas it makes sense to keep it smaller in cities.
- Ensure it works good internationally!
How it's been done?
What's your poison?
In the interest of speed, my goto tools for web applications are a serverless express api with s3 hosted react app and mongodb on atlas.
I'm also a fan of lerna so that's been used to tie it all together along with standardjs linting and cypress for integration tests (of which there currently are non π )
Sort the devops.
One thing I've learnt recently is it is much easier to decide on and implement a workflow and pipeline from the start than to create it retrospectively.
.gitlab-ci really does the business here and provides free access to an easy to use, highly integrated and fully featured build environment. Combine that with the serverless framework's ability to generate aws infrastructure and you've got environments created dynamically based on branch name, take a peak (Note the extra goodies included at the start to give some additional build insight) π
image: node:12.16
include:
- template: SAST.gitlab-ci.yml
- template: Dependency-Scanning.gitlab-ci.yml
- template: License-Scanning.gitlab-ci.yml
stages:
- test
- deploy
# Test and lint all branches
test:
stage: test
script:
- echo "Running tests.."
- npm install
- npx lerna clean --yes
- npx lerna bootstrap
- npm test
# Deploy all code on protected branches
deploy:
stage: deploy
before_script:
- npm config set prefix /usr/local
- npm install -g serverless
- serverless --version
script:
- cd packages/api
- envFile=${CI_COMMIT_BRANCH}_env
- cp ${!envFile} .env
- npm install
- serverless deploy --stage $CI_COMMIT_BRANCH --verbose
- cd ../web-app
- cp ${!envFile} .env
- npm install
- npm run build
- serverless deploy --stage $CI_COMMIT_BRANCH --verbose
environment:
name: $CI_COMMIT_BRANCH
only:
refs:
- branches
variables:
- $CI_COMMIT_REF_PROTECTED == "true"
Pen to paper.
You can see the full code here
Interesting bits
π Finding users within a radius
const users = await models.User.find({ address: { $geoWithin: { $centerSphere: [[parseFloat(location[0]), parseFloat(location[1])], radius / 6371.230] } } })
β‘οΈ Using pusher to see new job posts in real-time
//BACK-END
//private- channels requrie auth to subscribe
const channel = 'private-jobs'
pusher.trigger(channel, 'new_job', { _id: newJob._id, coordinates: newJob.target.address.coordinates })
//FRONT-END
const channel = pusher.subscribe('private-jobs')
channel.bind('new_job', async (job) => {
const distanceBetween = computeDistanceBetween([userHomeLoc.lat, userHomeLoc.lng], job.coordinates)
if (distanceBetween <= process.env.REACT_APP_MAXIMUM_JOB_RANGE_KM || isNaN(distanceBetween)) {
const { data } = await axios.get(`/jobs/id/${job._id}`)
setJobs(oldJobs => [...oldJobs, data])
}
})
π± Turning off platform notifications by replying to sms. We added this following endpoint to the incoming sms webhook on our Twilio number and secured it with a key.
try {
const { from, body } = req.body
if (body === 'STOP' || body === 'stop') {
const result = await UserService.toggleUserAvailabilityPhone(from, false)
return res.status(200).json(result)
}
return res.status(400).send(`Body "${body}" not supported in hook`)
} catch (e) {
return res.status(500).send(tidyError(e))
}
Comments.
This was a fun project, hopefully it finds some users and people that are keen to work on, and grow it!
Top comments (0)