I am not sure if I'm missing something here but I find that the accepted answer is more complicated than is necessary.
I see that db has to be hit to validate or invalidate a token for each api request, however the total process could have been simpler as I see things here.
Whenever a jwt is created, i.e. during login or change/reset password, insert the jwt with userid into a table and maintain a jti (a uuid number basically) for each jwt. The same jti goes into jwt payload too. Effectively jti uniquely identifies a jwt. A user can have multiple jwts at the same time when the account is accessed from multiple devices or browsers in which case, jti differentiates the device or the user-agent.
So the table schema would be, jti | userId. (and a primary key ofcourse)
For each api, check if the jti is in the table, which means the jwt is a valid one.
When the user changes or resets the password, delete all the jti of that userId from the db. Create and insert a new jwt with a new jti into the table. This will invalidate all the sessions from all other devices and browsers except the one that changed or reset the password.
When the user logsout, delete that particular jti of that user but not all. There would be a Single Login but not a single Logout. So when the user logs out, he shouldnt be logged out from all the devices. However, deleting all the jtis would logout from all the devices too.
So it would be one table and no date comparisons. Also it would be the same case if a refresh token is used or not.
However to minimize the db interference, and possible delays, cache usage would certainly help to ease things on processing time front.
Note: Please reason if you are down voting it.