This is a personal website that I built to exhibit my interests, projects and latest resume. In fact, this website is a by-product of RESTful API that I built underneath it. The website is hosted on AWS. Since I work most comfortably with Python, I choose Flask as my web framework and SQLAlchemy ORM as toolkit to interact with database. Nginx and Supervisor(uWSGI) are used to host web and API servers and keep them alive. I am currently working on Redis and Docker integration to make data retrieval faster and deployment smoother.
##RESTful API The RESTful API is built around personal information on my resume. The API includes the following categories:
user
: A user is a single entity containing basic personal information.skill
: A skill is a single technique.work_place
: A work_place is a work location.workplace_task
: A work_task is a task associated to a work_place.project
: A project is any related work done in and out of the school.project_task
: A project_task is a task associated to a project.school
: A school is an education institution.tag
: A tag is a category of skills.address
: An address is a place where the user lives.
Currently I only open requests to GET
method. All API support retrieving data through index
and find
.
index
: Supply resource id.find
: Supply a list of query parameters.
In addition, each API has its own deref
fields that can be added to the query string optionaly to return information in more detailed. To access the API, you can easily send GET
request to www.arthur-jen.com.
More examples will be provied below.
Retrieve user information.
index
: /api/user/<int:id
>find
: /api/user/?<string:email
;string:firstName
;string:lastName
;int:phone
>deref
fields:address
,skills
,workPlaces
,projects
, andschools
Examples:
-
/api/user/1
{ status: { statusMsg: "OK", statusDetails: {}, statusCode: "HTTPOk" }, result: { id: 1, name: "Fei-Yang Jen", email: "[email protected]", alt_email: "[email protected]", phone: "(226) 972-0522", skype: "arthur5110", github: "https://github.com/FYJen", linkedin: "https://www.linkedin.com/pub/arthur-jen/56/528/914" } }
-
/api/user/?email=[email protected]&deref=address&deref=projects
{ status: { statusMsg: "OK", statusDetails: {}, statusCode: "HTTPOk" }, result: [{ id: 1, name: "Fei-Yang Jen", email: "[email protected]", alt_email: "[email protected]", phone: "(226) 972-0522", skype: "arthur5110", github: "https://github.com/FYJen", linkedin: "https://www.linkedin.com/pub/arthur-jen/56/528/914", address: [{ id: 1, Apt / Suite / Floor: "Suite 302", streetName: "321 Lester St.", city: "Waterloo", province / state: "ON", country: "Canada", postalCode / zip: "N2L 3W6", active: true, occupants: [], stringnifyAddr: "Suite 302, 321 Lester St., Waterloo, ON, Canada, N2L 3W6" }], projects: [{ id: 1, name: "Personal Website", startDate: "7/2014", endDate: null, thumbnail: "/static/img/projects/personal_website.png", link: "https://github.com/FYJen/website", tasks: [ "Implemented RESTful API to serve internal requests", "Hosted on AWS and built with Python, Flask framework, SQLAlchemy, Jinja2 Template, Redis, SQLite, Bootstrap, HTML, CSS, Nginx and uWSGI" ] }, ..... ] }] }
Retrieve work places information.
index
: /api/workplace/<int:id
>find
: /api/workplace/?<string:name
;string:initial
;string:positionTitle
>deref
fields:user
,tasks
, andaddress
Examples:
-
/api/workplace/2
{ status: { statusMsg: "OK", statusDetails: {}, statusCode: "HTTPOk" }, result: { id: 3, name: "Ontario Institute for Cancer Research", initial: "OICR", positionTitle: "Cloud Computing Software Developer", startDate: "1/2013", endDate: "4/2013", web_link: "http://www.oicr.on.ca" } }
-
/api/workplace/?initial=OICR&deref=user&deref=tasks
{ status: { statusMsg: "OK", statusDetails: {}, statusCode: "HTTPOk" }, result: [{ id: 3, name: "Ontario Institute for Cancer Research", initial: "OICR", positionTitle: "Cloud Computing Software Developer", startDate: "1/2013", endDate: "4/2013", web_link: "http://www.oicr.on.ca", user: "[email protected]", tasks: [ "Improved software Installation time by 80% using Bash", "Built applications to deploy scalable SGE Cluster with NFS on GCE using Perl", "Created generic application to launch scalable environments on AWS" ] }] }
Retrieve work tasks information.
index
: /api/workplace/<int:id
>find
: /api/workplace/?<string:workPlaceName
;string:initial
>deref
fields:workplace
Examples:
-
/api/worktask/11
{ status: { statusMsg: "OK", statusDetails: {}, statusCode: "HTTPOk" }, result: { id: 11, description: "Implemented load-balancer using HAproxy, Dstat and third-party libraries" } }
-
/api/worktask/?initial=NCHC&deref=workplace
{ status: { statusMsg: "OK", statusDetails: {}, statusCode: "HTTPOk" }, result: [{ id: 10, description: "Configured DNS with geo-location feature to distribute client connections", workPlace: "National Center for High-Performance Computing" }, { id: 11, description: "Implemented load-balancer using HAproxy, Dstat and third-party libraries", workPlace: "National Center for High-Performance Computing" }, { id: 12, description: "Co-Developed nation-wide Cloud storage service", workPlace: "National Center for High-Performance Computing" }] }
Retrieve address information.
index
: /api/workplace/<int:id
>find
: /api/workplace/?<boolean:active
;string:country
;string:postalCode
;string:zipCode
;boolean:stringnify
>deref
fields:occupants
Examples:
-
/api/address/1
{ status: { statusMsg: "OK", statusDetails: {}, statusCode: "HTTPOk" }, result: { id: 1, Apt / Suite / Floor: "Suite 302", streetName: "321 Lester St.", city: "Waterloo", province / state: "ON", country: "Canada", postalCode / zip: "N2L 3W6", active: true, occupants: [] } }
-
/api/address/?active=1&country=USA&deref=occupants&stringnify=true
{ status: { statusMsg: "OK", statusDetails: {}, statusCode: "HTTPOk" }, result: [{ id: 3, Apt / Suite / Floor: "4th Floor", streetName: "153 Kearny St.", city: "San Francisco", province / state: "CA", country: "USA", postalCode / zip: "94108", active: true, occupants: [ "Inkling" ], stringnifyAddr: "4th Floor, 153 Kearny St., San Francisco, CA, USA, 94108" }] }
Retrieve tag information.
index
: /api/tag/<int:id
>find
: /api/tag/?<string:name
>deref
fields:skill
Examples:
-
/api/tag/5
{ status: { statusMsg: "OK", statusDetails: {}, statusCode: "HTTPOk" }, result: [{ id: 5, tagName: "Python" }] }
-
/api/tag/?name=Server&deref=skill
{ status: { statusMsg: "OK", statusDetails: {}, statusCode: "HTTPOk" }, result: [{ id: 7, tagName: "Server", skills: [{ id: 30, description: "Apache" }, { id: 31, description: "Nginx" }, { id: 32, description: "DNS" }, { id: 33, description: "HAproxy" }, { id: 34, description: "Jenkins CI Server"a }, { id: 35, description: "Azkaban Server" }] }] }
Retrieve skill information.
index
: /api/skill/<int:id
>find
: /api/skill/?<string:description
;string:tag
;int:userId
>deref
fields:user
andtag
Examples:
-
/api/skill/5
{ status: { statusMsg: "OK", statusDetails: {}, statusCode: "HTTPOk" }, result: [{ id: 5, description: "MySQL" }] }
-
/api/skill/?userId=1&deref=user&deref=tag
{ status: { statusMsg: "OK", statusDetails: {}, statusCode: "HTTPOk" }, result: [{ .... }, { id: 4, description: "SQLite", user: "[email protected]", tag: "Database" }, { id: 5, description: "MySQL", user: "[email protected]", tag: "Database" }, { id: 6, description: "PostgreSQL", user: "[email protected]", tag: "Database" }, { id: 7, description: "Feature implementations, APIs, performance optimizations and bug fixes", user: "[email protected]", tag: "Python" }, { .... }] }
Retrieve school information.
index
: /api/school/<int:id
>find
: /api/school/?<string:name
;string:level
;string:attending
>deref
fields:address
andcourse
(currently not supported)
Examples:
-
/api/school/1
{ status: { statusMsg: "OK", statusDetails: {}, statusCode: "HTTPOk" }, result: [{ id: 1, name: "University of Waterloo", level: "University/College", degree: "Bachelor of Computer Science", major: "Computer Science", minor: "Economics", joint: null, startDate: "9/2010", endDate: "8/2015", attending: true, term: "4A" }] }
-
/api/school/?attending=true&deref=address
{ status: { statusMsg: "OK", statusDetails: {}, statusCode: "HTTPOk" }, result: [{ id: 1, name: "University of Waterloo", level: "University/College", degree: "Bachelor of Computer Science", major: "Computer Science", minor: "Economics", joint: null, startDate: "9/2010", endDate: "8/2015", attending: true, term: "4A", address: [{ id: 2, Apt / Suite / Floor: "", streetName: "200 University Ave W.", city: "Waterloo", province / state: "ON", country: "Canada", postalCode / zip: "N2L 3G1", active: true, occupants: [], stringnifyAddr: "200 University Ave W., Waterloo, ON, Canada, N2L 3G1" }] }] }
Retrieve project information.
index
: /api/project/<int:id
>find
: /api/project/?<string:projectName
>deref
fields:user
andtasks
Examples:
-
/api/project/1
{ status: { statusMsg: "OK", statusDetails: {}, statusCode: "HTTPOk" }, result: { id: 1, name: "Personal Website", startDate: "7/2014", endDate: null, thumbnail: "/static/img/projects/personal_website.png", link: "https://github.com/FYJen/website" } }
-
/api/school/?attending=true&deref=address
{ status: { statusMsg: "OK", statusDetails: {}, statusCode: "HTTPOk" }, result: [{ id: 1, name: "Personal Website", startDate: "7/2014", endDate: null, thumbnail: "/static/img/projects/personal_website.png", link: "https://github.com/FYJen/website", tasks: [ "Implemented RESTful API to serve internal requests", "Hosted on AWS and built with Python, Flask framework, SQLAlchemy, Jinja2 Template, Redis, SQLite, Bootstrap, HTML, CSS, Nginx and uWSGI" ] }] }
Retrieve project task information.
index
: /api/projecttask/<int:id
>find
: /api/projecttask/?<string:projectName
>deref
fields:project
Examples:
-
/api/projecttask/1
{ status: { statusMsg: "OK", statusDetails: {}, statusCode: "HTTPOk" }, result: { id: 1, description: "An application that will curl a given YouTube playlist and download individual songs by posting requests to youtube-mp3.org" } }
-
/api/projecttask/?projectName=YouTube%20Playlist%20Curler&deref=project
{ status: { statusMsg: "OK", statusDetails: {}, statusCode: "HTTPOk" }, result: [{ id: 1, description: "An application that will curl a given YouTube playlist and download individual songs by posting requests to youtube-mp3.org", project: "YouTube Playlist Curler" }, { id: 2, description: "Built with Python and Google YouTube Data API (V3)", project: "YouTube Playlist Curler" }] }
##Database Model Database model can be found in dbmodels directory. The file defines a list of models(tables) used to store information for the website. API will be accessing these data through the models defined there. I choose to use SQLite to remove overhead dealing with configuring MySQL and PostgreSQL. Also, for the project like this, which is relatively less data-intensive, SQLite is a good candidate.
##Deployment
#####Local Development
Simply open two terminals and execute python run-api.py
and python run-web.py
separately. This will bring up both web and API server. By default, web server will run on 127.0.0.1:8080 and API server will run on 127.0.0.1:5050
####Production Nginx and Supersivor(uWSGI) are used to host web and API servers. The configurations for both Nginx and Supervisor can be found in deploy directory. I used Supervisor to bring up two separate uWSGI processes: one for webpages and one for API. Nginx is the front web server which will route traffic to different endpoints depending on the URL path.
##TODO There are a couple improvements that can be done to help out with deployment and overall user experience.
- Using memory database like Redis or Memcache
- Using Docker to deploy web server